package com.jack.code.auto.controller;

import com.jack.code.auto.domain.CreateCode;
import com.jack.code.auto.domain.request.CreateCodeRequestModel;
import com.jack.code.auto.domain.request.ModelColumn;
import com.jack.code.auto.domain.request.TableColumn;
import com.jack.code.auto.service.CreateCodeService;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 代码生成器
 * Created by LiJiakui on 17/3/2.
 */
@Controller
@RequestMapping("/createCode")
public class CreateCodeController {

    @Resource
    private CreateCodeService createCodeService;
    /**
     * 获取列表页
     * @return
     */
    @RequestMapping("/list")
    public ModelAndView list(){
        ModelAndView mv = new ModelAndView();
        List<CreateCode> list = createCodeService.list();
        mv.addObject("dataList",list);
        mv.setViewName("createCode/list");
        return mv;
    }

    /**
     * 跳转到代码生成界面
     * @return
     */
    @RequestMapping("/goProductionCode")
    public ModelAndView goProductionCode(){
        ModelAndView mv = new ModelAndView();

        mv.setViewName("createCode/production_code");
        return mv;
    }

    /**
     * 跳转到新增属性页面
     * @return
     */
    @RequestMapping("/goAddAttr")
    public ModelAndView goAddAttr(){
        ModelAndView mv = new ModelAndView();
        String uuid = UUID.randomUUID().toString().replace("-","");
        mv.addObject("uuid",uuid);
        mv.setViewName("createCode/add_attr");
        return mv;
    }

    @RequestMapping("/goEditAttr")
    public ModelAndView goEditAttr(String name,String type,String length,String remark,String isInput,String defaultVal,String uuid){
        ModelAndView mv = new ModelAndView();
        mv.addObject("uuid",uuid);
        mv.addObject("name",name);
        mv.addObject("type",type);
        mv.addObject("length",length);
        mv.addObject("remark",remark);
        mv.addObject("isInput",isInput);
        mv.addObject("defaultVal",defaultVal);
        mv.setViewName("createCode/edit_attr");
        return mv;
    }

    @RequestMapping("/delete")
    @ResponseBody
    public Map<String,Boolean> delete(Integer id){
        Map<String,Boolean> map = new HashMap<>();
        try {
            createCodeService.delete(id);
            map.put("success",true);
        } catch (Exception e) {
            e.printStackTrace();
            map.put("success",false);
        }

        return map;
    }

    @RequestMapping("/createCodeFile")
    public void createCodeFile(CreateCodeRequestModel model,HttpServletResponse response) throws Exception {

        model.setObjectNameLower(model.getObjectName().substring(0, 1).toLowerCase() + model.getObjectName().substring(1));
        model.setNowDate(new Date());
        if(StringUtils.isBlank(model.getPackageName())){
            model.setReadPackageName(".read");
            model.setWritePackageName(".write");
        }else{
            model.setPackageName("."+model.getPackageName());
            model.setReadPackageName(model.getPackageName()+"Read");
            model.setWritePackageName(model.getPackageName()+"Write");
        }

        //表名
        model.setTableName(model.getObjectName().replaceAll("[A-Z]","_$0").toLowerCase().substring(1));

        List<TableColumn> tableColumns = model.getColumns();
        List<ModelColumn> attrNameLower = model.getModelColumns();

        for(int i=0; i<model.getAttrNames().length;i++){
            //属性名
            String field = model.getAttrNames()[i].substring(0, 1).toLowerCase()+model.getAttrNames()[i].substring(1);
            //属性类型
            String attrType = model.getAttrTypes()[i];
            //属性说明
            String attrRemark = model.getAttrRemarks()[i];
            //首字母大写的属性名
            attrNameLower.add(new ModelColumn(field,field.substring(0, 1).toUpperCase() + field.substring(1),attrType,attrRemark));
            //添加类属性名与数据库字段名的映射
            tableColumns.add(new TableColumn(field.replaceAll("[A-Z]","_$0").toLowerCase(),field));
        }

        model.setHttpUrlName(model.getTableName().toUpperCase());

        StringBuffer httpFields = new StringBuffer("id");
        for(String httpField : model.getAttrNames()){
            httpFields.append(","+httpField);
        }
        model.setHttpFields(httpFields.toString());
        String uuid = getOrderIdByUUId();
        //存放路径
        String filePath = "out/"+uuid+"/code/";
        //ftl路径
        String ftlPath = "createCode/admin-server";
        String packageName = "admin-server";
        if(StringUtils.isNotBlank(model.getPackageName())){
            packageName = model.getPackageName().substring(1);
        }

        //server controller template
        printFile("controllerTemplate.ftl",model,packageName+"/"+model.getObjectName()+"Controller.java",filePath,ftlPath);

        //server service interface template
        printFile("serviceTemplate.ftl",model,packageName+"/"+model.getObjectName()+"Service.java",filePath,ftlPath);

        //server service impl template
        printFile("serviceImplTemplate.ftl",model,packageName+"/"+model.getObjectName()+"ServiceImpl.java",filePath,ftlPath);

        //server read mapper template
        printFile("readMapperTemplate.ftl",model,packageName+"/R"+model.getObjectName()+"Mapper.java",filePath,ftlPath);

        //server write mapper template
        printFile("writeMapperTemplate.ftl",model,packageName+"/W"+model.getObjectName()+"Mapper.java",filePath,ftlPath);

        //server read mapper xml template
        printFile("readMapperXMLTemplate.ftl",model,packageName+"/R"+model.getObjectName()+"Mapper.xml",filePath,ftlPath);

        //server write mapper xml template
        printFile("writeMapperXMLTemplate.ftl",model,packageName+"/W"+model.getObjectName()+"Mapper.xml",filePath,ftlPath);

        //server model template
        printFile("modelTemplate.ftl",model,packageName+"/"+model.getObjectName()+"Model.java",filePath,ftlPath);

        //server 部署说明
        printFile("doc.ftl",model,packageName+"/部署说明.txt",filePath,ftlPath);

        printFile("mysqlTemplate.ftl",model,packageName+"/"+model.getObjectNameLower()+"_SQL.sql",filePath,ftlPath);


        ftlPath = "createCode/admin-web";
        packageName = "admin-web";
        //server model template
        printFile("web_list.ftl",model,packageName+"/"+model.getObjectNameLower()+"/"+model.getObjectNameLower()+"_list.jsp",filePath,ftlPath);

        printFile("web_edit.ftl",model,packageName+"/"+model.getObjectNameLower()+"/"+model.getObjectNameLower()+"_edit.jsp",filePath,ftlPath);

        printFile("web_add.ftl",model,packageName+"/"+model.getObjectNameLower()+"/"+model.getObjectNameLower()+"_add.jsp",filePath,ftlPath);

        printFile("web_controller.ftl",model,packageName+"/"+model.getObjectName()+"Controller.java",filePath,ftlPath);

        printFile("modelTemplate.ftl",model,packageName+"/"+model.getObjectName()+"Model.java",filePath,ftlPath);

        printFile("Iconstants.ftl",model,packageName+"/部署说明.txt",filePath,ftlPath);

        printFile("mysqlTemplate.ftl",model,packageName+"/权限_SQL.sql",filePath,ftlPath);
        createCodeService.save(model);
        /*生成的全部代码压缩成zip文件*/
        if(zip(getClasspath()+filePath, getClasspath()+ "out/"+uuid+"/code.zip")){
			/*下载代码*/
            fileDownload(response, getClasspath()+"out/"+uuid+"/code.zip", model.getDescription()+".zip");
        }


    }

    /**获取classpath1
     * @return
     */
    public static String getClasspath(){//+"../../"
        String path = (String.valueOf(Thread.currentThread().getContextClassLoader().getResource(""))).replaceAll("file:/", "").replaceAll("%20", " ").trim();
        if(path.indexOf(":") != 1){
            path = File.separator + path;
        }
        return path;
    }

    public static String getTopLower(String objectName){
        char[] chars=new char[1];
        chars[0]=objectName.charAt(0);
        String temp=new String(chars);
//        if(chars[0]>='A'  &&  chars[0]<='Z'){
//
//        }
        return objectName.replace(temp,temp.toLowerCase());
    }

    /**
     * 输出到输出到文件
     * @param ftlName   ftl文件名
     * @param dataModel	传入的数据模型
     * @param outFile	输出后的文件全部路径
     * @param filePath	输出前的文件上部路径
     */
    public static void printFile(String ftlName, Object dataModel, String outFile, String filePath, String ftlPath) throws Exception{
        try {
            File file = new File(getClasspath() + filePath + outFile);
            if(!file.getParentFile().exists()){				//判断有没有父路径，就是判断文件整个路径是否存在
                file.getParentFile().mkdirs();				//不存在就全部创建
            }
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8"));
            Template template = getTemplate(ftlName, ftlPath);
            template.process(dataModel, out);					//模版输出
            out.flush();
            out.close();
        } catch (TemplateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过文件名加载模版
     * @param ftlName
     */
    public static Template getTemplate(String ftlName, String ftlPath) throws Exception{
        try {
            Configuration cfg = new Configuration();  												//通过Freemaker的Configuration读取相应的ftl
            cfg.setEncoding(Locale.CHINA, "utf-8");
            cfg.setDirectoryForTemplateLoading(new File(getClasspath()+"/ftl/"+ftlPath));		//设定去哪里读取相应的ftl模板文件
            Template temp = cfg.getTemplate(ftlName);												//在模板文件目录中找到名称为name的文件
            return temp;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Boolean zip(String inputFileName, String zipFileName) throws Exception {
        zip(zipFileName, new File(inputFileName));
        return true;
    }
    private static void zip(String zipFileName, File inputFile) throws Exception {
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName));
        zip(out, inputFile, "");
        out.flush();
        out.close();
    }

    private static void zip(ZipOutputStream out, File f, String base) throws Exception {
        if (f.isDirectory()) {
            File[] fl = f.listFiles();
            out.putNextEntry(new ZipEntry(base + "/"));
            base = base.length() == 0 ? "" : base + "/";
            for (int i = 0; i < fl.length; i++) {
                zip(out, fl[i], base + fl[i].getName());
            }
        } else {
            out.putNextEntry(new ZipEntry(base));
            FileInputStream in = new FileInputStream(f);
            int b;
            //System.out.println(base);
            while ((b = in.read()) != -1) {
                out.write(b);
            }
            in.close();
        }
    }

    /**
     * @param response
     * @param filePath		//文件完整路径(包括文件名和扩展名)
     * @param fileName		//下载后看到的文件名
     * @return  文件名
     */
    public static void fileDownload(final HttpServletResponse response, String filePath, String fileName) throws Exception{

        byte[] data = toByteArray2(filePath);
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.reset();
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream;charset=UTF-8");
        OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
        outputStream.write(data);
        outputStream.flush();
        outputStream.close();
        response.flushBuffer();

    }

    /**
     * 读取到字节数组2
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    public static byte[] toByteArray2(String filePath) throws IOException {
        File f = new File(filePath);
        if (!f.exists()) {
            throw new FileNotFoundException(filePath);
        }
        FileChannel channel = null;
        FileInputStream fs = null;
        try {
            fs = new FileInputStream(f);
            channel = fs.getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocate((int) channel.size());
            while ((channel.read(byteBuffer)) > 0) {
                // do nothing
                // System.out.println("reading");
            }
            return byteBuffer.array();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fs.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static String getOrderIdByUUId() {
        int machineId = 1;//最大支持1-9个集群机器部署
        int hashCodeV = UUID.randomUUID().toString().hashCode();
        if(hashCodeV < 0) {//有可能是负数
            hashCodeV = - hashCodeV;
        }
        // 0 代表前面补充0
        // 4 代表长度为4
        // d 代表参数为正数型
        return machineId + String.format("%015d", hashCodeV);
    }
    public static void main(String[] args) {
        System.out.println(getOrderIdByUUId());
    }
}
